package com.mathdroid.gui.main.talents;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import com.mathdroid.gui.ImageLoader;
import com.mathdroid.gui.main.DroidComboBoxModel;
import com.mathdroid.gui.main.EGlobalStrings;
import com.mathdroid.model.Model;
import com.mathdroid.model.talents.Spec;
import com.mathdroid.model.talents.Specs;
import com.mathdroid.persistence.Serializer;

/**
 * This panel gives the option to load, delete and save custom or default
 * character talents. Each talent tree is separated in its own tab.
 */
public class TalentsPanel extends JPanel {
	/**
	 * Default UID.
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * This ComboBox shows a list of saved talent tree templates.
	 */
	private final JComboBox templates = new JComboBox(new DroidComboBoxModel());
	
	/**
	 * A button to save the current talent tree configuration under the name
	 * in the ComboBox.
	 */
	private JButton save = new JButton();
	
	/**
	 * A button to delete the current selected configuration in the ComboBox.
	 */
	private JButton delete = new JButton();
	
	/**
	 * This TalentTree object is a JTabbedPane and contains the three trees.
	 */
	private TalentTreeTabbedPane talentTreeTabbedPane 
		= new TalentTreeTabbedPane();
	
	/**
	 * The Serializer used in this panel.
	 */
	private final Serializer serializer = new Serializer();
	
	/**
	 * The weight of "huge" components.
	 */
	private static final int HUGE_WEIGHT = 100;
	
	/**
	 * This is a panel inside this panel. It is necessary to have this because
	 * of the use of BorderLayout. The content should be in the NORTH, not in
	 * the center.
	 */
	private final JPanel content = new JPanel();
	
	/**
	 * The image loader.
	 */
	private final ImageLoader il = new ImageLoader();
	
	/**
	 * The constraints for this UI components.
	 */
	private GridBagConstraints c = new GridBagConstraints();
	
	/**
	 * The space between components.
	 */
	private static final int SPACING = 10;
	
	/**
	 * The width of the grid in columns.
	 */
	private static final int GRID_WIDTH = 3;
	
	/**
	 * The edge length of the save/delete buttons.
	 */
	private static final int BUTTON_EDGE = 25;
	
	/**
	 * Standard constructor.
	 */
	public TalentsPanel() {	
		this.setLayout(new BorderLayout());
		this.content.setLayout(new GridBagLayout());
		this.add(this.content, BorderLayout.NORTH);

		this.buildTemplatesComboBox();
		this.buildSaveButton();
		this.buildDeleteButton();
		this.buildTalentTreeTabbedPane();
	}

	/**
	 * Build the talent trees JTabbedPane on the bottom of this panel.
	 */
	private void buildTalentTreeTabbedPane() {
		/* Talent tree JTabbedPane */
		c.insets = new Insets(0, SPACING, SPACING, SPACING);
		c.gridwidth = GRID_WIDTH;
		c.gridx = 0;
		c.gridy = 1;
		this.content.add(this.talentTreeTabbedPane, c);
	}


	/**
	 * Build the delete button to delete templates.
	 */
	private void buildDeleteButton() {
		
		this.delete.setToolTipText(EGlobalStrings.DELETE_TEMPLATE.toString());
		this.delete.setIcon(this.il.getImage("delete"));
		this.delete.setPreferredSize(new Dimension(BUTTON_EDGE, BUTTON_EDGE));
		c.fill = GridBagConstraints.NONE;
		c.insets = new Insets(SPACING, 0, SPACING, SPACING);
		c.gridx = 2;
		addDeleteActionListener();
		this.content.add(this.delete, c);
	}


	/**
	 * Adds an action listener to the delete button.
	 */
	private void addDeleteActionListener() {
		this.delete.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				
				Object o = templates.getSelectedItem();
				
				if (o instanceof String) {
					Specs specs = serializer.getTalentSets(Model.getESpec());
					specs.remove(o.toString());
					serializer.serialize(specs, Model.getESpec().toString());
					
					templates.removeItem(o);
					templates.configureEditor(templates.getEditor(), null);
				}
			}
		});
	}

	/**
	 * Build a save button to save templates.
	 */
	private void buildSaveButton() {
		this.save.setToolTipText(EGlobalStrings.SAVE_TEMPLATE.toString());
		this.save.setIcon(this.il.getImage("add"));
		this.save.setPreferredSize(new Dimension(BUTTON_EDGE, BUTTON_EDGE));
		c.fill = GridBagConstraints.NONE;
		c.insets = new Insets(SPACING, 0, SPACING, SPACING);
		c.gridx = 1;
		c.weightx = 1;
		c.gridy = 0;
		c.gridwidth = 1;
		this.save.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				saveTemplate();
				refreshTemplates();
			}
		});
		this.content.add(this.save, c);
	}

	/**
	 * Build a combo box to select and edit templates.
	 */
	private void buildTemplatesComboBox() {
		this.templates.setEditable(true);
		this.refreshTemplates();
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		c.weightx = HUGE_WEIGHT;
		c.insets = new Insets(SPACING, SPACING, SPACING, SPACING);
		c.fill = GridBagConstraints.HORIZONTAL;
		this.content.add(this.templates, c);
		
		this.addTemplatesActionListener();
		this.addTemplatesKeyListener();
	}

	/**
	 * Adds a key listener to the editor of the combo box. Typing keys will
	 * disable the delete button. The enter key will save the new template.
	 */
	private void addTemplatesKeyListener() {
		this.templates.getEditor().getEditorComponent().addKeyListener(
				new KeyListener() {
			@Override
			public void keyPressed(final KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					saveTemplate();
					refreshTemplates();
				} else {
					delete.setEnabled(false);
				}
			}
			@Override
			public void keyReleased(final KeyEvent e) { }
			@Override
			public void keyTyped(final KeyEvent e) { }
		});
	}
	
	/**
	 * Refresh the combo box model.
	 */
	private void refreshTemplates() {
		this.templates.removeAllItems();
		Specs t = this.serializer.getTalentSets(Model.getESpec());
		
		for (String s : t.keySet()) {
			this.templates.addItem(s);
		}
	}

	/**
	 * Adds an action listener to the combo box. Selected templates will be
	 * loaded.
	 */
	private void addTemplatesActionListener() {
		this.templates.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				Object o = templates.getSelectedItem();
				
				if (e.getModifiers() == 16) {
					if (o instanceof String) {
						Spec spec = serializer.getTalentSets(
								Model.getESpec()).get(o.toString());
						if (spec != null) {
							Model.setSpec(spec);
						}
						delete.setEnabled(true);
					}
				}
			}
		});
	}
	

	/**
	 * Is called if the whole talent tree model changed.
	 */
	public final void modelChanged() {
		this.refreshTemplates();
		this.talentTreeTabbedPane.modelChanged();
		
		this.templates.configureEditor(
					templates.getEditor(), 
					Model.getProfile().getSpecTemplate().getName());
	}

	/**
	 * Save the template.
	 */
	private void saveTemplate() {
		Object o = templates.getEditor().getItem();
		String name = null;
		
		if (o instanceof String) {
			name = (String) o;
			
			if (!name.equals("")) {
				name = this.removeTrailingBrackets(name);
				Specs s = this.serializer.getTalentSets(Model.getESpec());
				Spec spec = Model.getProfile().getSpecTemplate();
				spec.setName(name);
				Model.setSpec(spec);
				
				/* Put the template into the persistence. */
				s.put(spec.toString(), spec);
				this.serializer.serialize(s, Model.getESpec().toString());
				
				try {
					JOptionPane.showMessageDialog(Model.getDroidFrame(),
							"Saved talents template \"" + name + "\".\n"
							+ "This template is shared between all \"" 
							+ Model.getESpec().toString() 
							+ "\" profiles you create.",
							"Talents saved", 
							JOptionPane.INFORMATION_MESSAGE);
					
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							templates.configureEditor(templates.getEditor(), 
									Model.getProfile()
									.getSpecTemplate().toString());
							delete.setEnabled(true);
						}
					});
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
	}

	/**
	 * Remove the brackets after a spec name.
	 * @param name The spec name.
	 * @return Substring.
	 */
	private String removeTrailingBrackets(final String name) {
		String substring = name;
		
		for (int i = 0; i < name.length(); i++) {
			if (name.toCharArray()[i] == '(') {
				substring = name.substring(0, (i - 1));
				break;
			}
		}
		return substring;
	}
}
